#include <gtest/gtest.h>
#include "NeuronTest.h"
#include <NeuralNetwork/Neuron/Neuron.h>
#include <NeuralNetwork/Neuron/Equation/EquationFactoryMock.h>
#include <NeuralNetwork/Neuron/Equation/EquationMock.h>
#include <NeuralNetwork/Neuron/Equation/SigmaEquation.h>

namespace tests {
    
    using ::testing::Return;
    using ::testing::_;
    
NeuronTest::NeuronTest() {
}

NeuronTest::~NeuronTest() {
}

void NeuronTest::SetUp() {
}

void NeuronTest::TearDown() {
}

TEST(NeuronTest, TestConstructorWithEquationFactory) {
    EquationMock<float>* eqMock = new EquationMock<float>;
    EquationFactoryMock<float>* fMock = new EquationFactoryMock<float>;
    EXPECT_CALL( *fMock, getInstance()).Times(1).WillRepeatedly(Return(eqMock));
     nn::Neuron<float> neuron( 5, utils::SharedPtr<nn::IEquationFactory<float> >(fMock));
     ASSERT_EQ( 5, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
}

TEST(NeuronTest, TestConstructorWithNullEquationFactoryShouldThrowException) {
     ASSERT_THROW(nn::Neuron<float>( 5, utils::SharedPtr<nn::IEquationFactory<float> >(NULL)), nn::NNException);
}

TEST(NeuronTest, TestConstructorWithEquationFactoryWhichReturnNullShouldThrowException)
{
    nn::IEquation<float>* equation = NULL;
    EquationFactoryMock<float>* fMock = new EquationFactoryMock<float>;
    EXPECT_CALL( *fMock, getInstance()).Times(1).WillRepeatedly(Return(equation));
    ASSERT_THROW(nn::Neuron<float>( 5, utils::SharedPtr<nn::IEquationFactory<float> >(fMock)), nn::NNException);
}

TEST(NeuronTest, TestConstructorWithEquationFactoryAndInputsNumberZeroShouldThrowException) {
    EquationMock<float>* eqMock = new EquationMock<float>;
    EquationFactoryMock<float>* fMock = new EquationFactoryMock<float>;
    ASSERT_THROW(nn::Neuron<float>( 0, utils::SharedPtr<nn::IEquationFactory<float> >(fMock)), nn::NNException);
}

TEST(NeuronTest, TestConstructorWithEquation) {
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( 5, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( 5, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
}

TEST(NeuronTest, TestConstructorWithNullEquationShouldThrowException) {
    nn::IEquation<float>* eqMock = NULL;
    ASSERT_THROW(nn::Neuron<float>( 5, utils::SharedPtr<nn::IEquation<float> >(eqMock)), nn::NNException);
}

TEST(NeuronTest, TestConstructorWithEquationAndZeroInputsNumberShouldThrowException) {
    nn::IEquation<float>* eqMock = new EquationMock<float>;
    ASSERT_THROW(nn::Neuron<float>( 0, utils::SharedPtr<nn::IEquation<float> >(eqMock)), nn::NNException);
}

TEST(NeuronTest, TestSetInputWeight) {
    const unsigned int inputsNumber =  rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
     
     for( int i =0; i< inputsNumber; i++){
         float inputWeight = rand();
         ASSERT_TRUE(neuron.setInputWeight(i, inputWeight));
         ASSERT_EQ( inputWeight, neuron.getInputWeight(i));
     }
}

TEST(NeuronTest, TestGetInputWeightWithInvalidInputIdShouldThrowException) {
    const unsigned int inputsNumber =  rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
     ASSERT_THROW(neuron.getInputWeight(inputsNumber+1), nn::NNException);
}

TEST(NeuronTest, TestSetInputWeightWithInvalidInputIdShouldReturnFalse) {
    const unsigned int inputsNumber = rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
     ASSERT_FALSE(neuron.setInputWeight(inputsNumber+1, 0.0f));
}

TEST(NeuronTest, TestGetEquation) {
    const unsigned int inputsNumber = rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
}

TEST(NeuronTest, TestSetEquation) {
    const unsigned int inputsNumber = rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
    EquationMock<float>* eqMock2 = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
     neuron.setEquation(eqMock2);
     ASSERT_TRUE( eqMock2== neuron.getEquation() );
}

TEST(NeuronTest, TestCalculateOutput) {
    const unsigned int inputsNumber =  rand()%1000 + 1;
    EquationMock<float>* eqMock = new EquationMock<float>;
     nn::Neuron<float> neuron( inputsNumber, utils::SharedPtr<nn::IEquation<float> >(eqMock));
     ASSERT_EQ( inputsNumber, neuron.getInputsNumber());
     ASSERT_EQ(eqMock, neuron.getEquation().getPtr() );
     
     const int result = rand()%1000;
     EXPECT_CALL( *eqMock,  calculateEquation(neuron.getNeuronWeight(), _) ).Times(1).WillRepeatedly(Return(result));
     ASSERT_TRUE(neuron.calculateOutput());
     ASSERT_EQ( result, neuron.getOutput());
}

}
// kate: indent-mode cstyle; space-indent on; indent-width 0; 
